<?php
// +----------------------------------------------------------------------
// | PHP [ JUST YOU ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2017 http://www.jyphp.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Albert <albert_p@foxmail.com>
// +----------------------------------------------------------------------
namespace JYPHP\ApiDoc;

use Illuminate\Session\SessionServiceProvider;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use JYPHP\Core\Console\Command as BaseCommand;
use JYPHP\Core\Controller;
use JYPHP\Core\Module;

class Command extends BaseCommand
{
    protected $name = "auto:apidoc";

    protected $desc = "操作HTTP服务";

    protected $files = [];

    protected $reflect;

    //存储 当前所读取模块所有控制器的API文档数据
    protected $temporary = [];

    //存储每个模块下的所有APIDocuments数据
    //结构 模块=>控制器=>接口
    protected $api = [];

    //过滤的系统接口
    protected $black = ['dealWithError', 'missMethod', 'toResponse'];

    //存储每个接口的字段是否必传
    protected $fieldType = [];

    public function configure()
    {
        parent::configure(); // TODO: Change the autogenerated stub
    }

    public function handle()
    {
        //render111
        $this->getController($this->app->modulesPath());
        $dir = $this->app->basePath() . "/apidoc";
        if (!is_dir($dir)) {
            mkdir($dir);
        }
        $resource = fopen($dir . "/api.json", "w");
        fwrite($resource,json_encode($this->api));
    }

    /**
     * 获取控制器
     * @param string $dir
     */
    public function getController(string $dir)
    {
        dir_each($dir, function ($file) use ($dir) {
            if (strpos($file, ".") !== 0) {
                $oldfile = $file;
                $file = "${dir}/${file}";
                if (is_dir($file) && $oldfile != 'Logics') {
                    $this->getController($file);
                } elseif ($oldfile != 'Logics') {
                    $file = str_replace("/", "\\", ltrim(str_replace($this->app->appPath(), "", str_replace(".php", "", $file)), "/"));
                    try {
                        $controller = $this->app->make($file);
                        if ($controller instanceof Controller) {
                            //控制器
                            $each = $this->getDocumentation($controller);
                            //如果改控制器的所属模块,还没有成立先存进去temporary
                            //否侧直接存进去所属模块下
                            if (!isset($this->api[$this->moduleName($file)])) {
                                $this->temporary[$this->getClassName($file)] = $each;
                            } else {
                                $this->api[$this->moduleName($file)]['interfaces'] = $each;
                            }
                            unset($each);
                        }
                        if ($controller instanceof Module) {
                            //模块
                            $this->api[$this->moduleName($file)] = [
                                'description' => $controller->description(),
                                'name' => $controller->name(),
                                'version' => $controller->version(),
                                'author' => $controller->author(),
                                'interfaces' => $this->temporary
                            ];
                            unset($file);
                            unset($controller);
                            unset($this->temporary);
                            unset($this->reflect);
                        }
                    } catch (\Exception $exception) {
                        echo "${file} make error " . $exception->getMessage() . "\n";
                    }
                }
            };
        });
    }

    /**
     * 获取控制器下方法的注释
     * @param $controller
     * @return array
     */
    public function getDocumentation(Controller $controller)
    {
        //每个接口的API数据
        $interfaces = [];
        $this->reflect = new \ReflectionObject($controller);
        foreach ($this->reflect->getMethods() as $key => $method) {
            if (empty($method->getDocComment()) || in_array($method->getName(), $this->black)) {
                continue;
            }
            //组合方法的字段是否属于必传
            $this->assemblage($method);
            $doc_comment = explode('*', str_replace("*/", '', str_replace("/**", '', $method->getDocComment())));
            foreach ($doc_comment as $value) {
                $value = trim($value, ' ');
                if (strpos($value, '@param') !== false) {
                    //接口参数
                    $interfaces[$method->getName()]['param'][] = $this->parseParam($value);
                } elseif (strpos($value, '@api') !== false) {
                    //接口名称
                    $interfaces[$method->getName()]['describe'] = $this->getDescribe($value);
                }
            }
            //接口URL
            $interfaces[$method->getName()]['url'] = $this->getClassName($this->reflect->getName()) . '/' . $method->getName();
            unset($this->fieldType);
        }
        unset($doc_comment);
        unset($controller);
        return $interfaces;
    }

    /**
     * 解析每个方法的@param
     * @param string $param
     * @return array
     */
    public function parseParam(string $param)
    {
        $param = explode(' ', trim($param, ' '));
        $filed = empty($param[2]) ? '' : trim(substr($param[2], 1));
        return [
            'field' => $filed,
            'type' => empty($param[1]) ? '' : $param[1],
            'describe' => empty($param[3]) ? '' : $param[3],
            'is_must' => empty($this->fieldType[$filed]) ? '' : $this->fieldType[$filed]
        ];
    }

    /**
     * 获取当前方法所属的控制器名,不带命名空间
     * @return mixed
     */
    public function getClassName(string $class_name)
    {
        $class_name = explode('\\', $class_name);
        return end($class_name);
    }

    /**
     * 获取接口描述
     * @param string $value
     * @return string
     */
    public function getDescribe(string $value)
    {
        $value = explode(' ', $value);
        return empty($value[1]) ? '' : $value[1];
    }

    /**
     * 组合当前方法的字段是否必传，用于解析@param的时候来判断
     * @param $method
     */
    public function assemblage($method)
    {
        foreach ($method->getParameters() as $value) {
            $this->fieldType[$value->name] = $value->isDefaultValueAvailable() ? '否' : '是';
        }
    }


    /**
     * 获取文件的模块名,Modules/xxx
     * @param string $value
     */
    public function moduleName(string $value)
    {
        $value = explode('\\',$value);
        return $value[0].'/'.$value[1];
    }
}